package org.checkboo.hopefullyengine.boundingvolumes;

import org.checkboo.hopefullyengine.sceneelements.BaseObject;
import org.checkboo.hopefullyengine.scenegraph.GeometryNode;
import org.checkboo.hopefullyengine.scenegraph.GroupNode;
import org.checkboo.hopefullyengine.scenegraph.Node;
import org.checkboo.math.Matrix44f;
import org.checkboo.math.Vector3f;

public class AxisAlignedBoundingBox implements BoundingVolume {

	private Vector3f v1, v2, v3, v4;
	private Vector3f v5, v6, v7, v8;

	public AxisAlignedBoundingBox() {
		this.v1 = new Vector3f();
		this.v2 = new Vector3f();
		this.v3 = new Vector3f();
		this.v4 = new Vector3f();
		this.v5 = new Vector3f();
		this.v6 = new Vector3f();
		this.v7 = new Vector3f();
		this.v8 = new Vector3f();
	}

	@Override
	public void updateBound(BaseObject object, Matrix44f transform) {
		float maxX, maxY, maxZ;
		float minX, minY, minZ;

		Vector3f init = new Vector3f(object.getVertices()[0]);
		init.transform(transform);
		Vector3f tmp = new Vector3f();
		maxX = minX = init.getX();
		maxY = minY = init.getY();
		maxZ = minZ = init.getZ();
		for (Vector3f vec : object.getVertices()) {
			if (null != vec) {
				tmp.set(vec);
				tmp.transform(transform);
				if (tmp.getX() > maxX)
					maxX = tmp.getX();
				else if (tmp.getX() < minX)
					minX = tmp.getX();

				if (tmp.getY() > maxY)
					maxY = tmp.getY();
				else if (tmp.getY() < minY)
					minY = tmp.getY();

				if (tmp.getZ() > maxZ)
					maxZ = tmp.getZ();
				else if (tmp.getZ() < minZ)
					minZ = tmp.getZ();
			}
		}

		v1.set(minX, maxY, maxZ);
		v2.set(maxX, maxY, maxZ);
		v3.set(minX, minY, maxZ);
		v4.set(maxX, minY, maxZ);
		v5.set(minX, maxY, minZ);
		v6.set(maxX, maxY, minZ);
		v7.set(minX, minY, minZ);
		v8.set(maxX, minY, minZ);

		// System.out.println(this.toString());

	}

	@Override
	public boolean updateBound(GroupNode node) {
		float maxX, maxY, maxZ;
		float minX, minY, minZ;
		GeometryNode gn = null;
		Vector3f tmp = new Vector3f();
		Vector3f init = null;
		for (Node n : node.getChildren()) {
			if (n instanceof GeometryNode) {
				init = new Vector3f(
						((AxisAlignedBoundingBox) ((GeometryNode) node
								.getChildren().getFirst()).getBoundingVolume()).v1);
				break;
			}
		}
		if (null == init){
			Vector3f point = node.getChildren().getFirst().getLocalTranslation();
			v1.set(point.getX(), point.getY(), point.getZ());
			v2.set(point.getX(), point.getY(), point.getZ());
			v3.set(point.getX(), point.getY(), point.getZ());
			v4.set(point.getX(), point.getY(), point.getZ());
			v5.set(point.getX(), point.getY(), point.getZ());
			v6.set(point.getX(), point.getY(), point.getZ());
			v7.set(point.getX(), point.getY(), point.getZ());
			v8.set(point.getX(), point.getY(), point.getZ());
			return true;
		}

		// init.transform(node.getWorldTransform());
		maxX = minX = init.getX();
		maxY = minY = init.getY();
		maxZ = minZ = init.getZ();
		for (Node n : node.getChildren()) {
			if (n instanceof GeometryNode) {
				gn = (GeometryNode) n;
				if (null == gn.getBoundingVolume()) {
					gn.setBoundingVolume(new AxisAlignedBoundingBox());
					gn.updateBound();
				}
				for (Vector3f vec : ((AxisAlignedBoundingBox) gn
						.getBoundingVolume()).getVertices()) {
					tmp.set(vec);
					// tmp.transform(node.getWorldTransform());
					if (tmp.getX() > maxX)
						maxX = tmp.getX();
					else if (tmp.getX() < minX)
						minX = tmp.getX();

					if (tmp.getY() > maxY)
						maxY = tmp.getY();
					else if (tmp.getY() < minY)
						minY = tmp.getY();

					if (tmp.getZ() > maxZ)
						maxZ = tmp.getZ();
					else if (tmp.getZ() < minZ)
						minZ = tmp.getZ();
				}
			}
			if (n instanceof GroupNode) {
				if (null == ((GroupNode) n).getBoundingVolume()) {
					((GroupNode) n).setBoundingVolume(new AxisAlignedBoundingBox());
					((GroupNode) n).updateBound();
				}
				for (Vector3f vec : ((AxisAlignedBoundingBox) ((GroupNode) n)
						.getBoundingVolume()).getVertices()) {
					tmp.set(vec);
					// tmp.transform(node.getWorldTransform());
					if (tmp.getX() > maxX)
						maxX = tmp.getX();
					else if (tmp.getX() < minX)
						minX = tmp.getX();

					if (tmp.getY() > maxY)
						maxY = tmp.getY();
					else if (tmp.getY() < minY)
						minY = tmp.getY();

					if (tmp.getZ() > maxZ)
						maxZ = tmp.getZ();
					else if (tmp.getZ() < minZ)
						minZ = tmp.getZ();
				}
			}
		}

		v1.set(minX, maxY, maxZ);
		v2.set(maxX, maxY, maxZ);
		v3.set(minX, minY, maxZ);
		v4.set(maxX, minY, maxZ);
		v5.set(minX, maxY, minZ);
		v6.set(maxX, maxY, minZ);
		v7.set(minX, minY, minZ);
		v8.set(maxX, minY, minZ);

		// System.out.println(node.getName()+"\n"+this.toString());
		return true;
	}

	@Override
	public boolean checkWithViewVolume(float[][] planes) {
		for (int i = 0; i < 6; i++) {
			if (planes[i][0] * v1.getX() + planes[i][1] * v1.getY()
					+ planes[i][2] * v1.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v2.getX() + planes[i][1] * v2.getY()
					+ planes[i][2] * v2.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v3.getX() + planes[i][1] * v3.getY()
					+ planes[i][2] * v3.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v4.getX() + planes[i][1] * v4.getY()
					+ planes[i][2] * v4.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v5.getX() + planes[i][1] * v5.getY()
					+ planes[i][2] * v5.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v6.getX() + planes[i][1] * v6.getY()
					+ planes[i][2] * v6.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v7.getX() + planes[i][1] * v7.getY()
					+ planes[i][2] * v7.getZ() + planes[i][3] > 0)
				continue;
			if (planes[i][0] * v8.getX() + planes[i][1] * v8.getY()
					+ planes[i][2] * v8.getZ() + planes[i][3] > 0)
				continue;
			return false;
		}
		return true;
	}

	public Vector3f getV1() {
		return v1;
	}

	public void setV1(Vector3f v1) {
		this.v1 = v1;
	}

	public Vector3f getV2() {
		return v2;
	}

	public void setV2(Vector3f v2) {
		this.v2 = v2;
	}

	public Vector3f getV3() {
		return v3;
	}

	public void setV3(Vector3f v3) {
		this.v3 = v3;
	}

	public Vector3f getV4() {
		return v4;
	}

	public void setV4(Vector3f v4) {
		this.v4 = v4;
	}

	public Vector3f getV5() {
		return v5;
	}

	public void setV5(Vector3f v5) {
		this.v5 = v5;
	}

	public Vector3f getV6() {
		return v6;
	}

	public void setV6(Vector3f v6) {
		this.v6 = v6;
	}

	public Vector3f getV7() {
		return v7;
	}

	public void setV7(Vector3f v7) {
		this.v7 = v7;
	}

	public Vector3f getV8() {
		return v8;
	}

	public void setV8(Vector3f v8) {
		this.v8 = v8;
	}

	public Vector3f[] getVertices() {
		Vector3f[] result = new Vector3f[8];
		result[0] = this.v1;
		result[1] = this.v2;
		result[2] = this.v3;
		result[3] = this.v4;
		result[4] = this.v5;
		result[5] = this.v6;
		result[6] = this.v7;
		result[7] = this.v8;

		return result;
	}

	public String toString() {
		String result = this.v1.toString() + "\n" + this.v2.toString() + "\n"
				+ this.v3.toString() + "\n" + this.v4.toString() + "\n"
				+ this.v5.toString() + "\n" + this.v6.toString() + "\n"
				+ this.v7.toString() + "\n" + this.v8.toString() + "\n";

		return result;
	}
}
